//********************************************************************************************
//*
//*    This file is part of Egoboo.
//*
//*    Egoboo is free software: you can redistribute it and/or modify it
//*    under the terms of the GNU General Public License as published by
//*    the Free Software Foundation, either version 3 of the License, or
//*    (at your option) any later version.
//*
//*    Egoboo is distributed in the hope that it will be useful, but
//*    WITHOUT ANY WARRANTY; without even the implied warranty of
//*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//*    General Public License for more details.
//*
//*    You should have received a copy of the GNU General Public License
//*    along with Egoboo.  If not, see <http://www.gnu.org/licenses/>.
//*
//********************************************************************************************

/// @file  game/Physics/ObjectPhysics.hpp
/// @brief Code for handling object physics
/// @author Johan Jansen aka Zefz
#pragma once

#include "idlib/idlib.hpp"
#include "egolib/egolib.h"

//Forward declarations
class Object;

namespace Ego
{
namespace Physics
{

class ObjectPhysics
{
public:
    ObjectPhysics(Object& object);

    /**
    * @brief
    *   Update a single physics tick for this Object.
    *   This integrates acceleration and velocities into new positions and
    *   includes collisions with the mesh.
    **/
    void updatePhysics();

    /// @details attach a character to a platform
    void detachFromPlatform();

    /**
    * @brief
    *   Attaches this Object to another Platform object
    **/
    bool attachToPlatform(const std::shared_ptr<Object> &platform);

    /**
    * @return
    *   Vector containing the desired XY velocity of the object
    *   Desired velocity in scaled space [-1 , 1]
    **/
    const Vector2f& getDesiredVelocity() const;

    /**
    * @brief
    *   Set desired velocity in XY scaled space [-1, 1]
    **/
    void setDesiredVelocity(const Vector2f &velocity);

    /**
    * @brief
    *   calculate a "mass" for an object, taking into account possible infinite masses.
    **/
    float getMass() const;

    /// @author ZZ
    /// @details This function makes the character pick up an item if there's one around
    bool grabStuff(grip_offset_t grip_off, bool grab_people);

    /**
    * @brief
    *   Attached this Object to another Object (e.g item into the hands of a holder or a
    *   character onto a mount)
    * @param holder
    *   Who we are to be attached to
    * @param grip_off
    *   In which hand should we be held?
    * @return
    *   true if this Object is now attached to the holder
    **/
    bool attachToObject(const std::shared_ptr<Object> &holder, grip_offset_t grip_off);

    /**
    * @author BB
    *
    * @details use this function to update the pchr->chr_max_cv and  pchr->chr_min_cv with
    *       values that reflect the best possible collision volume
    *
    * @note This function takes quite a bit of time, so it must only be called when the
    * vertices change because of an animation or because the matrix changes.
    *
    * @todo it might be possible to cache the src[] array used in this function.
    *   if the matrix changes, then you would not need to recalculate this data...
    **/
    void updateCollisionSize(bool updateMatrix);

    /**
    * @return
    *   true if the floor the Object is on is slippy.
    *   This means it is either icy or a hill you can slide down.
    **/
    bool floorIsSlippy() const;

    /**
    * @return
    *   true if the Object is touching the ground or standing on a platform
    **/
    bool isTouchingGround() const;

    /**
    * @return
    *   The actual level of the floor underneath the character
    */
    float getGroundElevation() const;

    /**
    * @return
    *   Simple 2-dimensional approximation of the object collision box
    *   for fast and rough collision detection.
    **/
    const AxisAlignedBox2f& getAxisAlignedBox2D() const;

private:
    /**
    * @brief
    *   Update voluntary movement generated by the Entity itself
    **/
    void updateMovement();

    /**
    * @brief
    *   Generate Z velocity (jumping, gravity, flight, etc.)
    **/
    void updateVelocityZ();
    
    /**
    * @brief
    *   This makes objects slide down hillsides
    **/
    void updateHillslide();

    /**
    * @brief
    *   This updates which way the Object is looking based on movement
    **/
    void updateFacing();

    /**
    * @return
    *   The actual max velocity of an Object including all abilities, special effects
    *   and any other speed modifiers
    **/
    float getMaxSpeed() const;
    
    /// @author BB
    /// @details Figure out the next position of the character.
    ///    Include collisions with the mesh in this step.
    void updateMeshCollision();

    /**
    * @brief
    *   This makes objects interact with the platform they are attached to (if any)
    **/
    void updatePlatformPhysics();

    /**
    * @brief
    *   This keeps held items in the hands of their holder and updates
    *   transfer blending
    **/
    void keepItemsWithHolder();

    /**
    * @return
    *   Number between 1 and 0 on how good contact we have with the floor
    *   Where 0.0f means full contact and 1.0f means no contact at all
    **/
    float getLerpZ() const;

    /**
    * @brief
    *   Recalculates the altitude of the ground beneath the Object. If
    *   standing on a platform, this is the platform altitude plus its height.
    *   If the Object can walk on water and is on a water tile, then this is the
    *   water level. Else it is the elevation of the ground mesh.
    **/
    float recalculateGroundElevation();

private:
    static constexpr float FLOOR_TOLERANCE = 20.0f;         //< Z tolerance for when we are touching the floor
    static constexpr float MAX_DISPLACEMENT_XY = 20.0f;     //< Max velocity correction due to being inside a wall
    static constexpr float TURNSPD = 0.1f;                  //< Cutoff for turning or same direction
    static constexpr uint16_t SPINRATE = 200;               //< How fast spinners spin
    static constexpr float WATCHMIN = 0.1f;                 //< Tolerance for TURNMODE_WATCH
    static constexpr float FLYDAMPEN = 0.001f;              ///< Levelling rate for flyers

    Object &_object;                //< The actual Entity that this physics class represents
    Vector2f _platformOffset;       //< Offset from the center of the platform we are standing on (if any) 
    Vector2f _desiredVelocity;      //< The requested velocity generated by player input or AI script
    float _traction;                //< How good grip do we have on the floor to generate movement?
    float _groundElevation;         //< Cached value of ground elevation below Object (could be platform or water)
    AxisAlignedBox2f _aabb2D;       //< 2-dimensional bounding box (for fast & rough collision detection)
};

} //Physics
} //Ego
